home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 February (DVD) / PCWorld_2008-02_DVD.iso / v cisle / PHP / PHP.exe / xampp-win32-1.6.5-installer.exe / php / PEAR / HTML / AJAX.php next >
Encoding:
PHP Script  |  2007-12-20  |  28.2 KB  |  870 lines

  1. <?php
  2. // $Id$
  3.  
  4. /**
  5.  * This is a quick hack, loading serializers as needed doesn't work in php5
  6.  */
  7. require_once "HTML/AJAX/Serializer/JSON.php";
  8. require_once "HTML/AJAX/Serializer/Null.php";
  9. require_once "HTML/AJAX/Serializer/Error.php";
  10. require_once "HTML/AJAX/Serializer/XML.php";
  11. require_once "HTML/AJAX/Serializer/PHP.php";
  12. require_once 'HTML/AJAX/Debug.php';
  13.     
  14. /**
  15.  * OO AJAX Implementation for PHP
  16.  *
  17.  * @category    HTML
  18.  * @package     AJAX
  19.  * @author      Joshua Eichorn <josh@bluga.net>
  20.  * @author      Arpad Ray <arpad@php.net>
  21.  * @author      David Coallier <davidc@php.net>
  22.  * @author      Elizabeth Smith <auroraeosrose@gmail.com>
  23.  * @copyright   2005 Joshua Eichorn, Arpad Ray, David Coallier, Elizabeth Smith
  24.  * @license     http://www.opensource.org/licenses/lgpl-license.php   LGPL
  25.  * @version     Release: 0.5.0
  26.  * @link        http://pear.php.net/package/HTML_AJAX
  27.  */
  28. class HTML_AJAX {
  29.     /**
  30.      * An array holding the instances were exporting
  31.      *
  32.      * key is the exported name
  33.      *
  34.      * row format is array('className'=>'','exportedName'=>'','instance'=>'','exportedMethods=>'')
  35.      *
  36.      * @var object
  37.      * @access private
  38.      */    
  39.     var $_exportedInstances = array();
  40.  
  41.     /**
  42.      * Set the server url in the generated stubs to this value
  43.      * If set to false, serverUrl will not be set
  44.      * @var false|string
  45.      */
  46.     var $serverUrl = false;
  47.  
  48.     /**
  49.      * What encoding your going to use for serializing data from php being sent to javascript
  50.      * @var string  JSON|PHP|Null
  51.      */
  52.     var $serializer = 'JSON';
  53.  
  54.     /**
  55.      * What encoding your going to use for unserializing data sent from javascript
  56.      * @var string  JSON|PHP|Null
  57.      */
  58.     var $unserializer = 'JSON';
  59.  
  60.     /**
  61.      * Option to use loose typing for JSON encoding
  62.      * @var bool
  63.      * @access public
  64.      */
  65.     var $jsonLooseType = true;
  66.  
  67.     /**
  68.      * Content-type map
  69.      *
  70.      * Used in to automatically choose serializers as needed
  71.      */
  72.     var $contentTypeMap = array(
  73.             'JSON'  => 'application/json',
  74.             'XML'   => 'application/xml',
  75.             'Null'  => 'text/plain',
  76.             'Error' => 'application/error',
  77.             'PHP'   => 'application/php-serialized',
  78.             'Urlencoded' => 'application/x-www-form-urlencoded'
  79.         );
  80.     
  81.     /**
  82.      * This is the debug variable that we will be passing the
  83.      * HTML_AJAX_Debug instance to.
  84.      *
  85.      * @param object HTML_AJAX_Debug
  86.      */
  87.     var $debug;
  88.  
  89.     /**
  90.      * This is to tell if debug is enabled or not. If so, then
  91.      * debug is called, instantiated then saves the file and such.
  92.      */
  93.     var $debugEnabled = false;
  94.     
  95.     /**
  96.      * This puts the error into a session variable is set to true.
  97.      * set to false by default.
  98.      *
  99.      * @access public
  100.      */
  101.     var $debugSession = false;
  102.  
  103.     /**
  104.      * If the Content-Length header should be sent, if your using a gzip handler on an output buffer, or run into any compatability problems, try disabling this.
  105.      *
  106.      * @access public
  107.      * @var boolean
  108.      */
  109.     var $sendContentLength = true;
  110.  
  111.     /**
  112.      * Make Generated code compatible with php4 by lowercasing all class/method names before exporting to JavaScript
  113.      * If you have code that works on php4 but not on php5 then setting this flag can fix the problem.   
  114.      * The recommended solution is too specify the class and method names when registering the class letting you have function case in php4 as well
  115.      *
  116.      * @access public
  117.      * @var boolean
  118.      */
  119.     var $php4CompatCase = false;
  120.  
  121.     /**
  122.      * Automatically pack all generated JavaScript making it smaller
  123.      *
  124.      * If your using output compression this might not make sense
  125.      */
  126.     var $packJavaScript = false;
  127.  
  128.     /**
  129.      * Holds current payload info
  130.      *
  131.      * @access private
  132.      * @var string
  133.      */
  134.     var $_payload;
  135.  
  136.     /**
  137.      * Holds iframe id IF this is an iframe xmlhttprequest
  138.      *
  139.      * @access private
  140.      * @var string
  141.      */
  142.     var $_iframe;
  143.  
  144.     /**
  145.      * Holds the list of classes permitted to be unserialized
  146.      *
  147.      * @access private
  148.      * @var array
  149.      */
  150.     var $_allowedClasses = array();
  151.     
  152.     /**
  153.      * Holds serializer instances
  154.      */
  155.     var $_serializers = array();
  156.     
  157.     /**
  158.      * PHP callbacks we're exporting
  159.      */
  160.     var $_validCallbacks = array();
  161.  
  162.     /**
  163.      * Set a class to handle requests
  164.      *
  165.      * @param   object  $instance
  166.      * @param   mixed   $exportedName   Name used for the javascript class, if false the name of the php class is used
  167.      * @param   mixed   $exportedMethods   If false all functions without a _ prefix are exported, if an array only the methods listed in the array are exported
  168.      * @return  void
  169.      */
  170.     function registerClass(&$instance, $exportedName = false, $exportedMethods = false)
  171.     {
  172.         $className = strtolower(get_class($instance));
  173.  
  174.         if ($exportedName === false) {
  175.             $exportedName = get_class($instance);
  176.             if ($this->php4CompatCase) {
  177.                 $exportedName = strtolower($exportedName);
  178.             }
  179.         }
  180.  
  181.         if ($exportedMethods === false) {
  182.             $exportedMethods = $this->_getMethodsToExport($className);
  183.         }
  184.  
  185.  
  186.         $index = strtolower($exportedName);
  187.         $this->_exportedInstances[$index] = array();
  188.         $this->_exportedInstances[$index]['className'] = $className;
  189.         $this->_exportedInstances[$index]['exportedName'] = $exportedName;
  190.         $this->_exportedInstances[$index]['instance'] =& $instance;
  191.         $this->_exportedInstances[$index]['exportedMethods'] = $exportedMethods;
  192.     }
  193.  
  194.     /**
  195.      * Get a list of methods in a class to export
  196.      *
  197.      * This function uses get_class_methods to get a list of callable methods, so if you're on PHP5 extending this class with a class you want to export should export its
  198.      * protected methods, while normally only its public methods would be exported. All methods starting with _ are removed from the export list.
  199.      * This covers PHP4 style private by naming as well as magic methods in either PHP4 or PHP5
  200.      *
  201.      * @param   string  $className
  202.      * @return  array   all methods of the class that are public
  203.      * @access private
  204.      */    
  205.     function _getMethodsToExport($className)
  206.     {
  207.         $funcs = get_class_methods($className);
  208.  
  209.         foreach ($funcs as $key => $func) {
  210.             if (strtolower($func) === $className || substr($func,0,1) === '_') {
  211.                 unset($funcs[$key]);
  212.             }
  213.             else if ($this->php4CompatCase) {
  214.                 $funcs[$key] = strtolower($func);
  215.             }
  216.         }
  217.         return $funcs;
  218.     }
  219.  
  220.     /**
  221.      * Generate the client Javascript code
  222.      *
  223.      * @return   string   generated javascript client code
  224.      */
  225.     function generateJavaScriptClient()
  226.     {
  227.         $client = '';
  228.  
  229.         $names = array_keys($this->_exportedInstances);
  230.         foreach ($names as $name) {
  231.             $client .= $this->generateClassStub($name);
  232.         }
  233.         return $client;
  234.     }
  235.  
  236.     /**
  237.      * Return the stub for a class
  238.      *
  239.      * @param    string   $name    name of the class to generated the stub for, note that this is the exported name not the php class name
  240.      * @return   string   javascript proxy stub code for a single class
  241.      */
  242.     function generateClassStub($name)
  243.     {
  244.         if (!isset($this->_exportedInstances[$name])) {
  245.             return '';
  246.         }
  247.  
  248.         $client = "// Client stub for the {$this->_exportedInstances[$name]['exportedName']} PHP Class\n";
  249.         $client .= "function {$this->_exportedInstances[$name]['exportedName']}(callback) {\n";
  250.         $client .= "\tmode = 'sync';\n";
  251.         $client .= "\tif (callback) { mode = 'async'; }\n";
  252.         $client .= "\tthis.className = '{$this->_exportedInstances[$name]['exportedName']}';\n";
  253.         if ($this->serverUrl) {
  254.             $client .= "\tthis.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,'{$this->serverUrl}','{$this->unserializer}');\n}\n";
  255.         } else {
  256.             $client .= "\tthis.dispatcher = new HTML_AJAX_Dispatcher(this.className,mode,callback,false,'{$this->unserializer}');\n}\n";
  257.         }
  258.         $client .= "{$this->_exportedInstances[$name]['exportedName']}.prototype  = {\n";
  259.         $client .= "\tSync: function() { this.dispatcher.Sync(); }, \n";
  260.         $client .= "\tAsync: function(callback) { this.dispatcher.Async(callback); },\n";
  261.         foreach($this->_exportedInstances[$name]['exportedMethods'] as $method) {
  262.             $client .= $this->_generateMethodStub($method);
  263.         }
  264.         $client = substr($client,0,(strlen($client)-2))."\n";
  265.         $client .= "}\n\n";
  266.  
  267.         if ($this->packJavaScript) {
  268.                 $client = $this->packJavaScript($client);
  269.         }
  270.         return $client;
  271.     }
  272.  
  273.     /**
  274.      * Returns a methods stub
  275.      *
  276.      *
  277.      * @param string the method name
  278.      * @return string the js code
  279.      * @access private
  280.      */    
  281.     function _generateMethodStub($method)
  282.     {
  283.         $stub = "\t{$method}: function() { return this.dispatcher.doCall('{$method}',arguments); },\n";
  284.         return $stub;
  285.     }
  286.  
  287.     /**
  288.      * Populates the current payload
  289.      *
  290.      *
  291.      * @param string the method name
  292.      * @return string the js code
  293.      * @access private
  294.      */    
  295.     function populatePayload()
  296.     {
  297.         if(isset($_REQUEST['Iframe_XHR'])) {
  298.             $this->_iframe = $_REQUEST['Iframe_XHR_id'];
  299.             if (isset($_REQUEST['Iframe_XHR_headers']) && is_array($_REQUEST['Iframe_XHR_headers'])) {
  300.                 foreach ($_REQUEST['Iframe_XHR_headers'] as $header) {
  301.                     $array = explode(':', $header);
  302.                     $array[0] = strip_tags(strtoupper(str_replace('-', '_', $array[0])));
  303.                     //only content-length and content-type can go in without an http_ prefix - security
  304.                     if(strpos($array[0], 'HTTP_') !== 0
  305.                           && strcmp('CONTENT_TYPE', $array[0])
  306.                           && strcmp('CONTENT_LENGTH', $array[0])) {
  307.                         $array[0] = 'HTTP_' . $array[0];
  308.                     }
  309.                     $_SERVER[$array[0]] = strip_tags($array[1]);
  310.                 }
  311.             }
  312.             $this->_payload = (isset($_REQUEST['Iframe_XHR_data']) ? $_REQUEST['Iframe_XHR_data'] : '');
  313.             if (isset($_REQUEST['Iframe_XHR_method'])) {
  314.                 $_GET['m'] = $_REQUEST['Iframe_XHR_method'];
  315.             }
  316.             if (isset($_REQUEST['Iframe_XHR_class'])) {
  317.                 $_GET['c'] = $_REQUEST['Iframe_XHR_class'];
  318.             }
  319.         }
  320.     }
  321.  
  322.     /**
  323.      * Handle a ajax request if needed
  324.      *
  325.      * The current check is if GET variables c (class) and m (method) are set, more options may be available in the future
  326.      *
  327.      * @todo is it worth it to figure out howto use just 1 instance if the type is the same for serialize and unserialize
  328.      *
  329.      * @return   boolean true if an ajax call was handled, false otherwise
  330.      */
  331.     function handleRequest()
  332.     {
  333.         set_error_handler(array(&$this,'_errorHandler'));
  334.         if (function_exists('set_exception_handler')) {
  335.             set_exception_handler(array(&$this,'_exceptionHandler'));
  336.         }
  337.         if (isset($_GET['px'])) {
  338.             if ($this->_iframeGrabProxy()) {
  339.                 restore_error_handler();
  340.                 if (function_exists('restore_exception_handler')) {
  341.                     restore_exception_handler();
  342.                 }
  343.                 return true;
  344.             }
  345.         }
  346.         
  347.         $class = strtolower($this->_getVar('c'));
  348.         $method = $this->_getVar('m');
  349.         $phpCallback = $this->_getVar('cb');
  350.         
  351.         if (!empty($class) && !empty($method)) {
  352.             if (!isset($this->_exportedInstances[$class])) {
  353.                 // handle error
  354.                 trigger_error('Unknown class: '. $class); 
  355.             }
  356.             if (!in_array($method,$this->_exportedInstances[$class]['exportedMethods'])) {
  357.                 // handle error
  358.                 trigger_error('Unknown method: ' . $method);
  359.             }
  360.         } else if (!empty($phpCallback)) {
  361.             if (strpos($phpCallback, '.') !== false) {
  362.                 $phpCallback = explode('.', $phpCallback);
  363.             }
  364.             if (!$this->_validatePhpCallback($phpCallback)) {
  365.                 restore_error_handler();
  366.                 if (function_exists('restore_exception_handler')) {
  367.                     restore_exception_handler();
  368.                 }
  369.                 return false;
  370.             }
  371.         } else {
  372.             restore_error_handler();
  373.             if (function_exists('restore_exception_handler')) {
  374.                 restore_exception_handler();
  375.             }
  376.             return false;
  377.         }
  378.  
  379.         // auto-detect serializer to use from content-type
  380.         $type = $this->unserializer;
  381.         $key = array_search($this->_getClientPayloadContentType(),$this->contentTypeMap);
  382.         if ($key) {
  383.             $type = $key;
  384.         }
  385.         $unserializer = $this->_getSerializer($type);
  386.  
  387.         $args = $unserializer->unserialize($this->_getClientPayload(), $this->_allowedClasses);
  388.         if (!is_array($args)) {
  389.             $args = array($args);
  390.         }
  391.         
  392.         if (empty($phpCallback)) {
  393.             $ret = call_user_func_array(array(&$this->_exportedInstances[$class]['instance'], $method), $args);
  394.         } else {
  395.             $ret = call_user_func_array($phpCallback, $args);
  396.         }
  397.         
  398.         restore_error_handler();
  399.         $this->_sendResponse($ret);
  400.         return true;
  401.     }
  402.  
  403.     /**
  404.      * Determines the content type of the client payload
  405.      *
  406.      * @return string
  407.      *   a MIME content type
  408.      */
  409.     function _getClientPayloadContentType()
  410.     {
  411.         //OPERA IS STUPID FIX
  412.         if (isset($_SERVER['HTTP_X_CONTENT_TYPE'])) {
  413.             $type = $this->_getServer('HTTP_X_CONTENT_TYPE');
  414.             $pos = strpos($type, ';');
  415.             return strtolower($pos ? substr($type, 0, $pos) : $type);
  416.         } else if (isset($_SERVER['CONTENT_TYPE'])) {
  417.             $type = $this->_getServer('CONTENT_TYPE');
  418.             $pos = strpos($type, ';');
  419.             return strtolower($pos ? substr($type, 0, $pos) : $type);
  420.         }
  421.         return 'text/plain';
  422.     }
  423.  
  424.     /**
  425.      * Send a reponse adding needed headers and serializing content
  426.      *
  427.      * Note: this method echo's output as well as setting headers to prevent caching
  428.      * Iframe Detection: if this has been detected as an iframe response, it has to
  429.      * be wrapped in different code and headers changed (quite a mess)
  430.      *
  431.      * @param    mixed content to serialize and send
  432.      * @access   private
  433.      */
  434.     function _sendResponse($response)
  435.     {
  436.         if(is_object($response) && is_a($response, 'HTML_AJAX_Response')) {
  437.             $output = $response->getPayload();
  438.             $content = $response->getContentType();
  439.         } else {
  440.             $serializer = $this->_getSerializer($this->serializer);
  441.             $output = $serializer->serialize($response);
  442.             if (isset($this->contentTypeMap[$this->serializer])) {
  443.                 //remember that IE is stupid and wants a capital T
  444.                 $content = $this->contentTypeMap[$this->serializer];
  445.             }
  446.         }
  447.         // headers to force things not to be cached:
  448.         $headers = array();
  449.         //OPERA IS STUPID FIX
  450.         if(isset($_SERVER['HTTP_X_CONTENT_TYPE']))
  451.         {
  452.             $headers['X-Content-Type'] = $content;
  453.             $content = 'text/plain';
  454.         }
  455.         if ($this->_sendContentLength()) {
  456.             $headers['Content-Length'] = strlen($output);
  457.         }
  458.         $headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
  459.         $headers['Last-Modified'] = gmdate( "D, d M Y H:i:s" ) . 'GMT';
  460.         $headers['Cache-Control'] = 'no-cache, must-revalidate';
  461.         $headers['Pragma'] = 'no-cache';
  462.         $headers['Content-Type'] = $content.'; charset=utf-8';
  463.  
  464.         //intercept to wrap iframe return data
  465.         if($this->_iframe)
  466.         {
  467.             $output = $this->_iframeWrapper($this->_iframe, $output, $headers);
  468.             $headers['Content-Type'] = 'text/html; charset=utf-8';
  469.         }
  470.         $this->_sendHeaders($headers);
  471.         echo $output;
  472.     }
  473.  
  474.     /**
  475.      * Decide if we should send a Content-length header
  476.      * @return   bool    true if it's ok to send the header, false otherwise
  477.      * @access   private
  478.      */
  479.     function _sendContentLength() {
  480.         if (!$this->sendContentLength) {
  481.             return false;
  482.         }
  483.         $ini_tests = array( "output_handler",
  484.                             "zlib.output_compression",
  485.                             "zlib.output_handler");
  486.         foreach($ini_tests as $test) {
  487.             if (ini_get($test)) {
  488.                 return false;
  489.             }
  490.         }
  491.         return (ob_get_level() <= 0);
  492.     }
  493.  
  494.     /**
  495.      * Actually send a list of headers
  496.      *
  497.      * @param   array list of headers to send, default callback for headers
  498.      * @access private
  499.      */
  500.     function _sendHeaders($array)
  501.     {
  502.         foreach($array as $header => $value) {
  503.             header($header . ': ' . $value);
  504.         }
  505.     }
  506.  
  507.     /**
  508.      * Get an instance of a serializer class
  509.      *
  510.      * @access private
  511.      */
  512.     function _getSerializer($type)
  513.     {
  514.         if (isset($this->_serializers[$type])) {
  515.             return $this->_serializers[$type];
  516.         }
  517.     
  518.         $class = 'HTML_AJAX_Serializer_'.$type;
  519.  
  520.         if ( (version_compare(phpversion(),5,'>') && !class_exists($class,false)) || (version_compare(phpversion(),5,'<') && !class_exists($class)) ) {
  521.             // include the class only if it isn't defined
  522.             require_once "HTML/AJAX/Serializer/{$type}.php";
  523.         }
  524.  
  525.         //handle JSON loose typing option for associative arrays
  526.         if ($type == 'JSON') {
  527.             $this->_serializers[$type] = new $class($this->jsonLooseType);
  528.         } else {
  529.             $this->_serializers[$type] = new $class();
  530.         }
  531.         return $this->_serializers[$type];
  532.     }
  533.  
  534.     /**
  535.      * Get payload in its submitted form, currently only supports raw post
  536.      *
  537.      * @access   private
  538.      * @return   string   raw post data
  539.      */
  540.     function _getClientPayload()
  541.     {
  542.         if (empty($this->_payload)) {
  543.             if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
  544.                 $this->_payload = $GLOBALS['HTTP_RAW_POST_DATA'];
  545.             } else if (function_exists('file_get_contents')) {
  546.                 // both file_get_contents() and php://input require PHP >= 4.3.0
  547.                 $this->_payload = file_get_contents('php://input');
  548.             } else {
  549.                 $this->_payload = '';
  550.             }
  551.         }
  552.         return $this->_payload;
  553.     }
  554.  
  555.     /**
  556.      * stub for getting get vars - applies strip_tags
  557.      *
  558.      * @access   private
  559.      * @return   string   filtered _GET value
  560.      */
  561.     function _getVar($var)
  562.     {
  563.         if (!isset($_GET[$var])) {
  564.             return NULL;
  565.         } else {
  566.             return strip_tags($_GET[$var]);
  567.         }
  568.     }
  569.  
  570.     /**
  571.      * stub for getting server vars - applies strip_tags
  572.      *
  573.      * @access   private
  574.      * @return   string   filtered _GET value
  575.      */
  576.     function _getServer($var)
  577.     {
  578.         if (!isset($_SERVER[$var])) {
  579.             return NULL;
  580.         } else {
  581.             return strip_tags($_SERVER[$var]);
  582.         }
  583.     }
  584.  
  585.     /**
  586.      * Exception handler, passes them to _errorHandler to do the actual work
  587.      *
  588.      * @access private
  589.      */
  590.     function _exceptionHandler($ex)
  591.     {
  592.         $this->_errorHandler($ex->getCode(),$ex->getMessage(),$ex->getFile(),$ex->getLine());
  593.     }
  594.       
  595.  
  596.     /**
  597.      * Error handler that sends it errors to the client side
  598.      *
  599.      * @access private
  600.      */
  601.     function _errorHandler($errno, $errstr, $errfile, $errline)
  602.     {
  603.         if ($errno & error_reporting()) {
  604.             $e = new stdClass();
  605.             $e->errNo   = $errno;
  606.             $e->errStr  = $errstr;
  607.             $e->errFile = $errfile;
  608.             $e->errLine = $errline;
  609.             $this->serializer = 'Error';
  610.             $this->_sendResponse($e);
  611.             if ($this->debugEnabled) {
  612.                 $this->debug =& new HTML_AJAX_Debug($errstr, $errline, $errno, $errfile);
  613.                 if ($this->debugSession) {
  614.                     $this->debug->sessionError();
  615.                 }
  616.                 $this->debug->_saveError();
  617.             }
  618.             die();
  619.         }
  620.     }
  621.  
  622.     /**
  623.      * Creates html to wrap serialized info for iframe xmlhttprequest fakeout
  624.      *
  625.      * @access private
  626.      */
  627.     function _iframeWrapper($id, $data, $headers = array())
  628.     {
  629.         $string = '<html><script type="text/javascript">'."\n".'var Iframe_XHR_headers = new Object();';
  630.         foreach($headers as $label => $value) {
  631.             $string .= 'Iframe_XHR_headers["'.preg_replace("/\r?\n/", "\\n", addslashes($label)).'"] = "'.preg_replace("/\r?\n/", "\\n", addslashes($value))."\";\n";
  632.         }
  633.         $string .= 'var Iframe_XHR_data = "' . preg_replace("/\r?\n/", "\\n", addslashes($data)) . '";</script>'
  634.             . '<body onload="parent.HTML_AJAX_IframeXHR_instances[\''.$id.'\']'
  635.             . '.isLoaded(Iframe_XHR_headers, Iframe_XHR_data);"></body></html>';
  636.         return $string;
  637.     }
  638.  
  639.     /**
  640.      * Handles a proxied grab request
  641.      *
  642.      * @return   bool    true to end the response, false to continue trying to handle it
  643.      * @access   private
  644.      */
  645.     function _iframeGrabProxy()
  646.     {
  647.         if (!isset($_REQUEST['Iframe_XHR_id'])) {
  648.             trigger_error('Invalid iframe ID');
  649.             return false;
  650.         }
  651.         $this->_iframe = $_REQUEST['Iframe_XHR_id'];
  652.         $this->_payload = (isset($_REQUEST['Iframe_XHR_data']) ? $_REQUEST['Iframe_XHR_data'] : '');
  653.         $url = urldecode($_GET['px']);
  654.         $url_parts = parse_url($url);
  655.         $urlregex = '#^https?://#i';
  656.         if (!preg_match($urlregex, $url) || $url_parts['host'] != $_SERVER['HTTP_HOST']) {
  657.             trigger_error('Invalid URL for grab proxy');
  658.             return true;
  659.         }
  660.         $method = (isset($_REQUEST['Iframe_XHR_HTTP_method'])
  661.             ? strtoupper($_REQUEST['Iframe_XHR_HTTP_method'])
  662.             : 'GET');
  663.         // validate method
  664.         if ($method != 'GET' && $method != 'POST') {
  665.             trigger_error('Invalid grab URL');
  666.             return true;
  667.         }
  668.         // validate headers
  669.         $headers = '';
  670.         if (isset($_REQUEST['Iframe_XHR_headers'])) {
  671.             foreach ($_REQUEST['Iframe_XHR_headers'] as $header) {
  672.                 if (strpos($header, "\r") !== false
  673.                         || strpos($header, "\n") !== false) {
  674.                     trigger_error('Invalid grab header');
  675.                     return true;
  676.                 }
  677.                 $headers .= $header . "\r\n";
  678.             }
  679.         }
  680.         // tries to make request with file_get_contents()
  681.         if (ini_get('allow_url_fopen') && version_compare(phpversion(), '5.0.0'. '>=')) {
  682.             $opts = array(
  683.                 $url_parts['scheme'] => array(
  684.                     'method'  => $method,
  685.                     'headers' => $headers,
  686.                     'content' => $this->_payload
  687.                 )
  688.             );
  689.             $ret = @file_get_contents($url, false, stream_context_create($opts));
  690.             if (!empty($ret)) {
  691.                 $this->_sendResponse($ret);
  692.                 return true;
  693.             }
  694.         }
  695.         // tries to make request using the curl extension
  696.         if (function_exists('curl_setopt')) {
  697.             $ch = curl_init();
  698.             curl_setopt($ch, CURLOPT_URL, $url);
  699.             curl_setopt($ch, CURLOPT_HEADER, $headers);
  700.             curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  701.             $ret = curl_exec($ch);
  702.             if ($ret !== false) {
  703.                 curl_close($ch);
  704.                 $this->_sendResponse($ret);
  705.                 return true;
  706.             }
  707.         }
  708.         if (isset($url_parts['port'])) {
  709.             $port = $url_parts['port'];
  710.         } else { 
  711.             $port = getservbyname(strtolower($url_parts['scheme']), 'tcp');
  712.             if ($port === false) {
  713.                 trigger_error('Grab proxy: Unknown port or service, defaulting to 80', E_USER_WARNING);
  714.                 $port = 80;
  715.             }
  716.         }
  717.         if (!isset($url_parts['path'])) {
  718.             $url_parts['path'] = '/';
  719.         }
  720.         if (!empty($url_parts['query'])) {
  721.             $url_parts['path'] .= '?' . $url_parts['query'];
  722.         }
  723.         $request = "$method {$url_parts['path']} HTTP/1.0\r\n"
  724.             . "Host: {$url['host']}\r\n"
  725.             . "Connection: close\r\n"
  726.             . "$headers\r\n";
  727.         // tries to make request using the socket functions
  728.         $fp = fsockopen($_SERVER['HTTP_HOST'], $port, $errno, $errstr, 4);
  729.         if ($fp) {
  730.             fputs($fp, $request);
  731.             $ret = '';
  732.             $done_headers = false;
  733.             while (!feof($fp)) {
  734.                 $ret .= fgets($fp, 2048);
  735.                 if ($done_headers || ($contentpos = strpos($ret, "\r\n\r\n")) === false) {
  736.                     continue;
  737.                 }
  738.                 $done_headers = true;
  739.                 $ret = substr($ret, $contentpos + 4);
  740.             }
  741.             fclose($fp);
  742.             $this->_sendResponse($ret);
  743.             return true;
  744.         }
  745.         // tries to make the request using the socket extension
  746.         $host = gethostbyname($url['host']);
  747.         if (($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0
  748.             || ($connected = socket_connect($socket, $host, $port)) < 0
  749.             || ($written = socket_write($socket, $request)) < strlen($request)) {
  750.              trigger_error('Grab proxy failed: ' . socket_strerror($socket));
  751.              return true;
  752.         }
  753.         $ret = '';
  754.         $done_headers = false;
  755.         while ($out = socket_read($socket, 2048)) {
  756.             $ret .= $out;
  757.             if ($done_headers || ($contentpos = strpos($ret, "\r\n\r\n")) === false) {
  758.                 continue;
  759.             }
  760.             $done_headers = true;
  761.             $ret = substr($ret, $contentpos + 4);
  762.         }
  763.         socket_close($socket);
  764.         $this->_sendResponse($ret);
  765.         return true;
  766.     }
  767.  
  768.     /**
  769.       * Add a class or classes to those allowed to be unserialized
  770.       *
  771.       * @param  mixed   $classes
  772.       *   the class or array of classes to add
  773.       * @access public
  774.       */
  775.     function addAllowedClasses($classes)
  776.     {
  777.         if (!is_array($classes)) {
  778.             $this->_allowedClasses[] = $classes;
  779.         } else {
  780.             $this->_allowedClasses = array_merge($this->_allowedClasses, $classes);
  781.         }
  782.         $this->_allowedClasses = array_unique($this->_allowedClasses);
  783.     }
  784.     
  785.     /**
  786.      * Checks that the given callback is callable and allowed to be called
  787.      *
  788.      * @param   callback    $callback
  789.      *  the callback to check
  790.      * @return  bool
  791.      *  true if the callback is valid, false otherwise
  792.      * @access  private
  793.      */
  794.     function _validatePhpCallback($callback)
  795.     {
  796.         if (!is_callable($callback)) {
  797.             return false;
  798.         }
  799.         $sig = md5(serialize($callback));
  800.         return isset($this->_validCallbacks[$sig]);
  801.     }
  802.     
  803.     /**
  804.      * Register a callback so it may be called from JS
  805.      * 
  806.      * @param   callback    $callback
  807.      *  the callback to register
  808.      * @access  public
  809.      */
  810.     function registerPhpCallback($callback)
  811.     {
  812.         $this->_validCallbacks[md5(serialize($callback))] = 1;
  813.     }
  814.  
  815.     /**
  816.      * Make JavaScript code smaller
  817.      * 
  818.      * Currently just strips whitespace and comments, needs to remain fast
  819.      */
  820.     function packJavaScript($input) {
  821.         $stripPregs = array(
  822.             '/^\s+$/',
  823.             '/^\s*\/\/.*$/'
  824.         );
  825.         $blockStart = '/\/\*/';
  826.         $blockEnd = '/\*\//';
  827.  
  828.         $out = '';
  829.  
  830.         $lines = explode("\n",$input);
  831.         $inblock = false;
  832.         foreach($lines as $line) {
  833.             $keep = true;
  834.             if ($inblock) {
  835.                 if (preg_match($blockEnd,$line)) {
  836.                     $inblock = false;
  837.                     $keep = false;
  838.                 }
  839.             }
  840.             else if (preg_match($blockStart,$line)) {
  841.                 $inblock = true;
  842.                 $keep = false;
  843.                 if (preg_match($blockEnd,$line)) {
  844.                     $inblock = false;
  845.                 }
  846.             }
  847.  
  848.             if (!$inblock) {
  849.                 foreach($stripPregs as $preg) {
  850.                     if (preg_match($preg,$line)) {
  851.                         $keep = false;
  852.                         break;
  853.                     }
  854.                 }
  855.             }
  856.  
  857.             if ($keep && !$inblock) {
  858.                 $out .= trim($line)."\n";
  859.             }
  860.             /* Enable to see what your striping out
  861.             else {
  862.                 echo $line."<br>";
  863.             }//*/
  864.         }
  865.         return $out;
  866.     }
  867. }
  868. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  869. ?>
  870.